package com.example.demo.handler;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.adapter.RequestDispatcher;
import com.example.demo.dto.Action;
import com.example.demo.dto.NettyHttpRequest;
import com.example.demo.dto.NettyHttpResponse;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Component
@ChannelHandler.Sharable
public class HeartbeatHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

	private static final Logger LOGGER = LoggerFactory.getLogger(HeartbeatHandler.class);

	@Autowired
	private RequestDispatcher dispatcher;

	/**
	 * 线程工厂
	 */
	private ExecutorService executor = Executors.newCachedThreadPool(runnable -> {
		Thread thread = Executors.defaultThreadFactory().newThread(runnable);
		thread.setName("NettyHttpHandler-" + thread.getName());
		return thread;
	});

	
	@Override
	public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
		LOGGER.info("register client and id:{}",ctx.channel().id());
		super.channelRegistered(ctx);
	}


	@Override
	public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
		LOGGER.info("unregister client and id:{}",ctx.channel().id());
		super.channelUnregistered(ctx);
	}


	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		LOGGER.info("active client and id:{}",ctx.channel().id());
		super.channelActive(ctx);
	}


	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		LOGGER.info("inactive client and id:{}",ctx.channel().id());
		super.channelInactive(ctx);
	}


	@Override
	public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
		LOGGER.info("writabilityChanged client and id:{}",ctx.channel().id());
		super.channelWritabilityChanged(ctx);
	}


	/**
	 * 异常处理
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		 cause.printStackTrace();
	     ctx.close();
		super.exceptionCaught(ctx, cause);
	}


	/**
	 * 超时处理
	 */
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if(evt instanceof IdleStateEvent) {
			IdleState state = ((IdleStateEvent) evt).state();
			LOGGER.info("5s have no get client msg,idle state:{}",state);
		}
		super.userEventTriggered(ctx, evt);
	}


	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) {
		ctx.flush();
	}


	@Override
	protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
		LOGGER.info("request id:{}, url:{}",ctx.channel().id(),request.uri());
		FullHttpRequest copyRequest = request.copy();
		/**
		 * 通过内部类的形式传入一个runnable的实现类并重写了run方法 线程池在执行的时候会调用这个方法
		 */
		executor.execute(() -> onReceivedRequest(ctx, new NettyHttpRequest(copyRequest)));
	}

	/**
	 *
	 * @param context
	 * @param request
	 */
	private void onReceivedRequest(ChannelHandlerContext context, NettyHttpRequest request) {
		/**
		 * 处理request请求
		 */
		FullHttpResponse response = handleHttpRequest(request);
		/**
		 * 通过channel将结果输出 并通过添加监听器的方式关闭channel通道
		 */
		context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
		/**
		 * 释放bytebuf缓存
		 */
		ReferenceCountUtil.release(request);
	}

	/**
	 *
	 * @param request NettyHttpRequest extends Fullhttpreuqest
	 * @return 请求处理结果
	 */
	private FullHttpResponse handleHttpRequest(NettyHttpRequest request) {
		try {
			Object response = dispatcher.dispatcher(request);
			return NettyHttpResponse.ok(JSONObject.toJSONString(response));
		} catch (Exception e) {
			e.printStackTrace();
			return NettyHttpResponse.makeError(e);
		}
	}
}
